package com.mytools.common.example.lock;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;

/**
 * @author LJK
 * @description
 * @date 2025/2/17
 */
public class MyAQS {
    //false表示无锁，true表示上锁了
    AtomicBoolean atomicBoolean = new AtomicBoolean(false);
    Thread owner = null;
    //让引用也具有CAS操作
    AtomicReference<Node> head = new AtomicReference<>(new Node());
    AtomicReference<Node> tail = new AtomicReference<>(head.get());

    void lock() {
        //公平锁：在上锁的时候先将线程放入队尾并使其阻塞，队首节点会成功拿到锁并割下头结点，让自己成为头结点（此时队首节点只有向后指的作用了）
        //在解锁的时候，首先将锁释放，才能让后续节点获得锁；然后唤醒当前节点的下一节点

        //非公平锁，新来的直接抢锁，抢成功了就不需要进入队列
//        if (atomicBoolean.compareAndSet(false, true)) {
//            owner = Thread.currentThread();
//            return;
//        }

        //将线程放到队尾
        Node curr = new Node();
        curr.thread = Thread.currentThread();
        while (true) {
            Node currTail = this.tail.get();
            if (tail.compareAndSet(currTail, curr)) {
                currTail.next = curr;
                curr.pre = currTail;
                break;
            }
        }
        //查看当前节点是否为头结点，是并且可以取到锁的话，就把当前节点的前驱节点割出去；如果不是队头，那就让当前节点阻塞，直到他被唤醒
        while (true) {
            if (curr.pre == head.get() &&
                    atomicBoolean.compareAndSet(false, true)) {
                owner=Thread.currentThread();
                head.set(curr);
                curr.pre.next=null;
                curr.pre=null;
                return;
            }
            //先检测能不能锁再阻塞的原因：可能存在某一时刻当前节点还没有与上一节点链成链表；此时上一节点已经准备释放锁并找下一节点。
            //但是由于链表还未构建完成，会导致这一节点持续阻塞无法被唤醒
            LockSupport.park();
        }
    }
    /*
    假设有head-->A-->B-->C-->null的场景
    在A执行lock的时候，变成了A-->B-->C-->null；执行unlock的时候，head就成了A，next就是B
    此时唤醒B线程，B会将链表变成B-->C-->null
    */
    void unlock() {
        if (Thread.currentThread() != owner) {//解决解锁线程和上锁线程不一致问题
            throw new RuntimeException("解锁线程为非法线程");
        }

        Node headNode = head.get();
        Node headNext = headNode.next;
        //在释放锁之前，所有的线程准备竞争锁时都需要把自己放在链表的尾部，并且被前驱指好
        //如果此时lock方法是先park再检查，可能会导致某一节点无法唤醒
        atomicBoolean.set(false);
        if(headNext!=null){
            LockSupport.unpark(headNext.thread);
        }

    }

    class Node {
        Node pre;
        Node next;
        Thread thread;
        Node() {
            this.pre = null;
            this.next = null;
            this.thread = null;
        }
    }
}
